/**
 * Json对象转字符串
 * @param o {object} 对象
 * @param fixKey {*|string}属性名称添加前缀【长度：0-10】，注：谨慎使用,注意属性名称带引号不要使用此参数
 * @returns {*}
 */
bs.jsonEncode = function (o, fixKey) {
    var isFunction = bs.util.isFunction,
        isObject = bs.util.isObject,
        isArray = bs.util.isArray,
        _toStr = Object.prototype.toString,
        UNDEFINED = "undefined",
        OBJECT = "object",
        NULL = "null",
        STRING = "string",
        NUMBER = "number",
        BOOLEAN = "boolean",
        DATE = "date",
        _allowable = {
            "undefined": UNDEFINED,
            string: STRING,
            "[object String]": STRING,
            number: NUMBER,
            "[object Number]": NUMBER,
            "boolean": BOOLEAN,
            "[object Boolean]": BOOLEAN,
            "[object Date]": DATE,
            "[object RegExp]": OBJECT
        },
        _SPECIAL_CHARS = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
        _CHARS = {
            "\b": "\\b",
            "\t": "\\t",
            "\n": "\\n",
            "\f": "\\f",
            "\r": "\\r",
            '"': '\\"',
            "\\": "\\\\"
        },
        dateToString = function (d) {
            function _zeroPad(v) {
                return v < 10 ? "0" + v : v
            }

            return d.getUTCFullYear() + "-" + _zeroPad(d.getUTCMonth() + 1) + "-" + _zeroPad(d.getUTCDate()) + "T" + _zeroPad(d.getUTCHours()) + ":" + _zeroPad(d.getUTCMinutes()) + ":" + _zeroPad(d.getUTCSeconds()) + "Z"
        };

    function _type(o) {
        var t = typeof o;
        return _allowable[t] || _allowable[_toStr.call(o)] || (t === OBJECT ? (o ? OBJECT : NULL) : UNDEFINED)
    }

    function _char(c) {
        if (!_CHARS[c]) {
            _CHARS[c] = "\\u" + ("0000" + ( +(c.charCodeAt(0))).toString(16)).slice(-4)
        }
        return _CHARS[c]
    }

    function _string(s) {
        return '"' + s.replace(_SPECIAL_CHARS, _char) + '"'
    }

    function _indent(s, space) {
        return s.replace(/^/gm, space)
    }

    function _stringify(o, space) {
        if (o === undefined) {
            return undefined
        }
        var w = null;
        var format = _toStr.call(space).match(/String|Number/) || [],
            stack = [];
        space = format[0] === "Number" ? new Array(Math.min(Math.max(0, space), 10) + 1).join(" ") : (space || "").slice(0, 10);
        function _serialize(h, key) {
            var value = h[key],
                t = _type(value),
                a = [],
                colon = space ? ": " : ":",
                arr,
                i,
                keys,
                k,
                v;
            if (isObject(value) && isFunction(value.toJSON)) {
                value = value.toJSON(key)
            } else {
                if (t === DATE) {
                    value = dateToString(value)
                }
            }
            if (value !== h[key]) {
                t = _type(value)
            }
            switch (t) {
                case DATE:
                case OBJECT:
                    break;
                case STRING:
                    return _string(value);
                case NUMBER:
                    return isFinite(value) ? value + "" : NULL;
                case BOOLEAN:
                    return value + "";
                case NULL:
                    return NULL;
                default:
                    return undefined
            }
            for (i = stack.length - 1; i >= 0; --i) {
                if (stack[i] === value) {
                    throw new Error("JSON.stringify. Cyclical reference")
                }
            }
            arr = isArray(value);
            stack.push(value);
            if (arr) {
                for (i = value.length - 1; i >= 0; --i) {
                    a[i] = _serialize(value, i) || NULL
                }
            } else {
                keys = w || value;
                i = 0;
                for (k in keys) {
                    if (keys.hasOwnProperty(k)) {
                        v = _serialize(value, k);
                        if (v) {
                            a[i++] = _string(k) + colon + v
                        }
                    }
                }
            }
            stack.pop();
            if (space && a.length) {
                return arr ? "[\n" + _indent(a.join(",\n"), space) + "\n]" : "{\n" + _indent(a.join(",\n"), space) + "\n}"
            } else {
                return arr ? "[" + a.join(",") + "]" : "{" + a.join(",") + "}"
            }
        }

        return _serialize({
                "": o
            },
            "")
    }

    return _stringify(o, fixKey)

};
/**
 *字符串转Json对象
 * @param s
 */
bs.jsonDecode = function (s) {
    var _UNICODE_EXCEPTIONS = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
        _ESCAPES = /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,
        _VALUES = /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,
        _BRACKETS = /(?:^|:|,)(?:\s*\[)+/g,
        _UNSAFE = /[^\],:{}\s]/,
        _escapeException = function (c) {
            return "\\u" + ("0000" + ( +(c.charCodeAt(0))).toString(16)).slice(-4)
        },
        _parse = function (s, reviver) {
            s = s.replace(_UNICODE_EXCEPTIONS, _escapeException);
            if (!_UNSAFE.test(s.replace(_ESCAPES, "@").replace(_VALUES, "]").replace(_BRACKETS, ""))) {
                return eval("(" + s + ")")
            }
            throw new SyntaxError("JSON.parse")
        };

    if (!bs.util.isString(s)) {
        s += "";
    }
    return _parse(s);
};